IPC와 스레드 메모리 공유 비교

NOTE

IPC와 스레드 메모리 공유는 “공유 방법”이 다른 게 아니라, 애초에 공유가 필요한 이유(주소 공간이 독립인가 아닌가) 자체가 다르다는 관점으로 정리한다.

1. 근본 원인: 주소 공간(Address Space)이 독립인가

프로세스스레드
주소 공간프로세스마다 독립 — 서로 다른 가상 주소 공간한 프로세스 안에서 공유 — 같은 가상 주소 공간
서로의 메모리기본적으로 완전히 격리, 못 봄이미 같은 공간을 보고 있음
”공유”하려면커널이 중재하는 별도 채널 필요 → IPC아무것도 안 해도 됨 (포인터만 넘기면 됨)

프로세스는 자기 안에서 쓰는 포인터 값이 다른 프로세스에겐 그냥 숫자일 뿐이다(같은 값이어도 그 프로세스의 가상 주소 공간에서 다른 물리 메모리를 가리키거나, 아예 매핑조차 안 되어 있음). 그래서 데이터를 넘기려면 커널이 개입해서 ①값을 복사해서 전달하거나(메시지 큐), ②아예 같은 물리 메모리를 양쪽의 가상 주소 공간에 매핑해주거나(공유메모리) 해야 한다. 이게 IPC가 필요한 이유의 전부다.

2. 프로세스 메모리 레이아웃과 스레드

한 프로세스의 메모리는 대략 이렇게 나뉜다.

높은 주소
┌─────────────┐
│   Stack     │  ← 함수 호출/지역변수. 스레드마다 별도!
├─────────────┤
│   (빈 공간)  │
├─────────────┤
│   Heap      │  ← malloc()/free(). 스레드 간 공유
├─────────────┤
│   BSS       │  ← 초기화 안 된 전역변수. 공유
│   Data      │  ← 초기화된 전역변수. 공유
│   Code/Text │  ← 실행 코드. 공유
└─────────────┘
낮은 주소

한 프로세스 안에 스레드를 여러 개 만들면(pthread_create 등), Stack(+ 레지스터, TLS)만 스레드별로 독립이고 나머지(Code/Data/BSS/Heap)는 전부 그대로 공유된다. 그래서:

  • malloc()으로 할당한 heap 메모리는 어느 스레드가 만들었든 포인터 값만 다른 스레드에 넘겨주면 그대로 접근 가능 — 별도 IPC 메커니즘 필요 없음
  • 전역 변수도 마찬가지로 그냥 공유됨

3. 그래서 IPC는 뭘로 하나

같은 물리 서버 안에서도 프로세스가 다르면 메모리가 격리되므로, 목적에 따라 아래 두 계열을 다르게 쓴다.

방식원리특징
메시지 큐커널이 데이터를 복사해서 전달 (송신 큐 → 수신 큐)이벤트/메시지 하나씩 주고받기에 적합 (송수신 타이밍이 분리되어도 큐에 쌓임)
공유메모리같은 물리 메모리를 여러 프로세스의 가상 주소 공간에 매핑 — 복사 없이 진짜로 같은 메모리를 봄대용량 데이터를 여러 프로세스가 계속 zero-copy로 들여다봐야 할 때 적합

"공유메모리로 공유한다"는 절반만 맞은 이야기일 수 있다

사내 프레임워크가 메시지 큐 기반 IPC 래퍼와 공유메모리 기반 인메모리 DB(또는 레지스트리)를 동시에 제공하는 경우가 흔하다. 둘은 목적이 다르다 — 메시지 큐 기반 래퍼는 데이터를 복사해서 주고받고, 공유메모리 기반 컴포넌트는 복사 없이 여러 프로세스가 같은 메모리를 직접 본다. 어떤 컴포넌트를 쓰고 있는지에 따라 “공유”의 의미가 다르다는 걸 헷갈리면 안 된다.

4. 레이스 컨디션과 동기화 — 스레드 vs 프로세스

“메모리가 공유된다”는 곧 “동시에 건드리면 꼬일 수 있다”는 뜻이다. 공유가 자동이든(스레드) 명시적으로 연결했든(공유메모리 IPC) 이 문제는 똑같이 생기고, 해결책도 같은 계열이다 — 다만 이름 없는(스레드용) 버전과 이름 있는(프로세스용) 버전으로 나뉜다.

스레드 (같은 프로세스)프로세스 (공유메모리로 연결)
도구pthread_mutex, 이름없는 세마포어(sem_init, pshared=0)System V 세마포어(semget), named 세마포어(sem_open)
왜 다른 버전이 필요한가뮤텍스 객체 자체가 그 프로세스의 heap/전역 메모리에 있으면 됨 — 어차피 다 같은 주소공간이라 포인터만 공유하면 접근 가능뮤텍스 객체를 프로세스 A의 heap에 두면 프로세스 B는 그 주소를 아예 못 봄 → 커널이 관리하는 **전역 키(key)**로 식별해야 함

메시지 큐는 이 문제에서 조금 비켜나 있다 — 큐에 대한 송수신 자체(msgsnd/msgrcv)가 커널 안에서 원자적으로 처리되기 때문에, “메시지 큐에 동시에 넣었다가 꼬인다”는 걱정을 사용자가 직접 할 필요가 적다. 반면 공유메모리는 그냥 날것의 메모리라서, 그 안의 데이터 구조를 여러 프로세스가 동시에 건드리면 스레드 때와 똑같이 락이 필요하다.

5. 한눈에 정리

IPC (프로세스 간)스레드 (프로세스 내)
근본 원인주소 공간이 독립적주소 공간이 이미 공유됨
공유 안 되는 부분전부 (기본 완전 격리)Stack, 레지스터, TLS만
데이터 주고받는 법커널 매개 (메시지 큐 / 공유메모리 / 파이프…)포인터만 넘기면 끝
동시성 제어named 세마포어 등 (System V)unnamed 뮤텍스/세마포어 (pthread)

관련 문서